using System.ComponentModel.DataAnnotations;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;

using OpenIddict.Abstractions;

using WL.Think.Abp.Dto;
using WL.Think.Abp.Localization;

namespace WL.Think.Abp.OpenIddict.Dtos;

/// <summary>
/// 应用创建和更新DTO
/// </summary>
public class ApplicationCreateUpdateDto : BaseEntityDto, IValidatableObject
{
    /// <summary>
    /// 客户端ID
    /// </summary>
    [Required]
    public string? ClientId { get; set; }
    /// <summary>
    /// 显示名称
    /// </summary>
    [Required]
    public string? DisplayName { get; set; }
    /// <summary>
    /// 客户端秘钥
    /// </summary>
    public string? ClientSecret { get; set; }
    /// <summary>
    /// 客户端地址
    /// </summary>
    public string? ClientUri { get; set; }
    /// <summary>
    /// Logo地址
    /// </summary>
    public string? LogoUri { get; set; }
    /// <summary>
    /// 客户端类型
    /// </summary>
    public string ClientType { get; set; } = OpenIddictConstants.ClientTypes.Public;
    /// <summary>
    /// 同意类型
    /// </summary>
    public string ConsentType { get; set; } = OpenIddictConstants.ConsentTypes.Implicit;
    /// <summary>
    /// 应用类型
    /// </summary>
    public string ApplicationType { get; set; } = OpenIddictConstants.ApplicationTypes.Web;
    /// <summary>
    /// 授权类型
    /// </summary>
    public List<string> GrantTypes { get; set; } = new List<string>();
    /// <summary>
    /// 重定向地址
    /// </summary>
    public List<string> RedirectUris { get; set; } = new List<string>();
    /// <summary>
    /// 登出重定向地址
    /// </summary>
    public List<string> PostLogoutRedirectUris { get; set; } = new List<string>();
    /// <summary>
    /// 权限
    /// </summary>
    public List<string> Scopes { get; set; } = new List<string>();


    /// <summary>
    /// 获取权限
    /// </summary>
    /// <returns></returns>
    public HashSet<string> GetPermissions()
    {
        var permissions = new HashSet<string>(StringComparer.Ordinal);
        if (new[] {
                OpenIddictConstants.GrantTypes.AuthorizationCode,
                OpenIddictConstants.GrantTypes.Implicit
            }.All(GrantTypes.Contains)
        )
        {
            permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken);
            if (string.Equals(ClientType, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
            {
                permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken);
                permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeToken);
            }
        }

        if (!RedirectUris.IsNullOrEmpty() || !PostLogoutRedirectUris.IsNullOrEmpty())
        {
            permissions.Add(OpenIddictConstants.Permissions.Endpoints.EndSession);
        }

        var buildInGrantTypes = new[] {
            OpenIddictConstants.GrantTypes.Implicit, OpenIddictConstants.GrantTypes.Password,
            OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.ClientCredentials,
            OpenIddictConstants.GrantTypes.DeviceCode, OpenIddictConstants.GrantTypes.RefreshToken
        };

        foreach (var grantType in GrantTypes)
        {
            if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode)
            {
                permissions.Add(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode);
                permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Code);
            }

            if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode || grantType == OpenIddictConstants.GrantTypes.Implicit)
            {
                permissions.Add(OpenIddictConstants.Permissions.Endpoints.Authorization);
            }

            if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode ||
                grantType == OpenIddictConstants.GrantTypes.ClientCredentials ||
                grantType == OpenIddictConstants.GrantTypes.Password ||
                grantType == OpenIddictConstants.GrantTypes.RefreshToken ||
                grantType == OpenIddictConstants.GrantTypes.DeviceCode)
            {
                permissions.Add(OpenIddictConstants.Permissions.Endpoints.Token);
                permissions.Add(OpenIddictConstants.Permissions.Endpoints.Revocation);
                permissions.Add(OpenIddictConstants.Permissions.Endpoints.Introspection);
            }

            if (grantType == OpenIddictConstants.GrantTypes.ClientCredentials)
            {
                permissions.Add(OpenIddictConstants.Permissions.GrantTypes.ClientCredentials);
            }

            if (grantType == OpenIddictConstants.GrantTypes.Implicit)
            {
                permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Implicit);
            }

            if (grantType == OpenIddictConstants.GrantTypes.Password)
            {
                permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Password);
            }

            if (grantType == OpenIddictConstants.GrantTypes.RefreshToken)
            {
                permissions.Add(OpenIddictConstants.Permissions.GrantTypes.RefreshToken);
            }

            if (grantType == OpenIddictConstants.GrantTypes.DeviceCode)
            {
                permissions.Add(OpenIddictConstants.Permissions.GrantTypes.DeviceCode);
                permissions.Add(OpenIddictConstants.Permissions.Endpoints.DeviceAuthorization);
            }

            if (grantType == OpenIddictConstants.GrantTypes.Implicit)
            {
                permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdToken);
                if (string.Equals(ClientType, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
                {
                    permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken);
                    permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Token);
                }
            }

            if (!buildInGrantTypes.Contains(grantType))
            {
                permissions.Add(OpenIddictConstants.Permissions.Prefixes.GrantType + grantType);
            }
        }

        var buildInScopes = new[] {
            OpenIddictConstants.Permissions.Scopes.Address, OpenIddictConstants.Permissions.Scopes.Email,
            OpenIddictConstants.Permissions.Scopes.Phone, OpenIddictConstants.Permissions.Scopes.Profile,
            OpenIddictConstants.Permissions.Scopes.Roles
        };

        foreach (var scope in Scopes)
        {
            if (buildInScopes.Contains(scope))
            {
                permissions.Add(scope);
            }
            else
            {
                permissions.Add(OpenIddictConstants.Permissions.Prefixes.Scope + scope);
            }
        }

        return permissions;
    }

    /// <summary>
    /// 扩展实体校验
    /// </summary>
    /// <param name="validationContext"></param>
    /// <returns></returns>
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var L = validationContext.GetRequiredService<IStringLocalizer<ThinkResource>>();

        if (!string.IsNullOrEmpty(ClientSecret) &&
            string.Equals(ClientType, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)
        )
        {
            yield return new ValidationResult(L["NoClientSecretCanBeSetForPublicApplications"], [nameof(ClientSecret), nameof(ClientType)]);
        }

        if (string.IsNullOrEmpty(ClientSecret) &&
            string.Equals(ClientType, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase))
        {
            yield return new ValidationResult(L["TheClientSecretIsRequiredForConfidentialApplications"], [nameof(ClientSecret), nameof(ClientType)]);
        }
    }
}
